From 1afc29d869f3928095864a0a05cd4a1753a488c1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 9 Jun 2011 20:45:20 +0200 Subject: [PATCH] win32: resurect Windows clipboard selection notification This is a rewrite of e6fa7394baa8a7cb80ae01a0c81729717019172b, with misc fixes that should help with some bugs Tim was talking about. https://bugzilla.gnome.org/show_bug.cgi?id=652239 --- gdk/win32/gdkdisplay-win32.c | 186 +++++++++++++++++++++++++++++++++-- gdk/win32/gdkevents-win32.c | 39 ++++---- gdk/win32/gdkprivate-win32.h | 1 + 3 files changed, 200 insertions(+), 26 deletions(-) diff --git a/gdk/win32/gdkdisplay-win32.c b/gdk/win32/gdkdisplay-win32.c index 8e7c9881b1..7c783d3991 100644 --- a/gdk/win32/gdkdisplay-win32.c +++ b/gdk/win32/gdkdisplay-win32.c @@ -79,7 +79,7 @@ enum_monitor (HMONITOR hmonitor, DWORD dwFlags; CHAR szDevice[CCHDEVICENAME]; } MONITORINFOEXA2; - + MONITORINFOEXA2 monitor_info; HDC hDC; @@ -252,7 +252,7 @@ gdk_win32_display_get_name (GdkDisplay *display) PFN_ProcessIdToSessionId processIdToSessionId; g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); - + if (display_name_cache != NULL) return display_name_cache; @@ -304,7 +304,7 @@ static gint gdk_win32_display_get_n_screens (GdkDisplay *display) { g_return_val_if_fail (GDK_IS_DISPLAY (display), 0); - + return 1; } @@ -341,15 +341,189 @@ gdk_win32_display_supports_selection_notification (GdkDisplay *display) { g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE); - return FALSE; + return TRUE; +} + +static HWND _hwnd_next_viewer = NULL; +static int debug_indent = 0; + +/* + * maybe this should be integrated with the default message loop - or maybe not ;-) + */ +static LRESULT CALLBACK +inner_clipboard_window_procedure (HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) +{ + switch (message) + { + case WM_DESTROY: /* remove us from chain */ + { + ChangeClipboardChain (hwnd, _hwnd_next_viewer); + PostQuitMessage (0); + return 0; + } + case WM_CHANGECBCHAIN: + { + HWND hwndRemove = (HWND) wparam; /* handle of window being removed */ + HWND hwndNext = (HWND) lparam; /* handle of next window in chain */ + + if (hwndRemove == _hwnd_next_viewer) + _hwnd_next_viewer = hwndNext == hwnd ? NULL : hwndNext; + else if (_hwnd_next_viewer != NULL) + return SendMessage (_hwnd_next_viewer, message, wparam, lparam); + + return 0; + } +#ifdef WM_CLIPBOARDUPDATE + case WM_CLIPBOARDUPDATE: +#endif + case WM_DRAWCLIPBOARD: + { + int success; + HWND hwndOwner; + UINT nFormat = 0; + GdkEvent *event; + GdkWindow *owner; + + success = OpenClipboard (hwnd); + g_return_val_if_fail (success, 0); + hwndOwner = GetClipboardOwner (); + owner = gdk_win32_window_lookup_for_display (_gdk_display, hwndOwner); + if (owner == NULL) + owner = gdk_win32_window_foreign_new_for_display (_gdk_display, hwndOwner); + + GDK_NOTE (DND, g_print (" drawclipboard owner: %p", hwndOwner)); + + if (_gdk_debug_flags & GDK_DEBUG_DND) + { + while ((nFormat = EnumClipboardFormats (nFormat)) != 0) + g_print ("%s ", _gdk_win32_cf_to_string (nFormat)); + } + + GDK_NOTE (DND, g_print (" \n")); + + event = gdk_event_new (GDK_OWNER_CHANGE); + event->owner_change.window = _gdk_root; + event->owner_change.owner = owner; + event->owner_change.reason = GDK_OWNER_CHANGE_NEW_OWNER; + event->owner_change.selection = GDK_SELECTION_CLIPBOARD; + event->owner_change.time = _gdk_win32_get_next_tick (0); + event->owner_change.selection_time = GDK_CURRENT_TIME; + _gdk_win32_append_event (event); + + CloseClipboard (); + + if (_hwnd_next_viewer != NULL) + return SendMessage (_hwnd_next_viewer, message, wparam, lparam); + + /* clear error to avoid confusing SetClipboardViewer() return */ + SetLastError (0); + return 0; + } + default: + /* Otherwise call DefWindowProcW(). */ + GDK_NOTE (EVENTS, g_print (" DefWindowProcW")); + return DefWindowProc (hwnd, message, wparam, lparam); + } +} + +static LRESULT CALLBACK +_clipboard_window_procedure (HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) +{ + LRESULT retval; + + GDK_NOTE (EVENTS, g_print ("%s%*s%s %p", + (debug_indent > 0 ? "\n" : ""), + debug_indent, "", + _gdk_win32_message_to_string (message), hwnd)); + debug_indent += 2; + retval = inner_clipboard_window_procedure (hwnd, message, wparam, lparam); + debug_indent -= 2; + + GDK_NOTE (EVENTS, g_print (" => %I64d%s", (gint64) retval, (debug_indent == 0 ? "\n" : ""))); + + return retval; +} + +/* + * Creates a hidden window and adds it to the clipboard chain + */ +static HWND +_gdk_win32_register_clipboard_notification (void) +{ + WNDCLASS wclass = { 0, }; + HWND hwnd; + ATOM klass; + + wclass.lpszClassName = "GdkClipboardNotification"; + wclass.lpfnWndProc = _clipboard_window_procedure; + wclass.hInstance = _gdk_app_hmodule; + + klass = RegisterClass (&wclass); + if (!klass) + return NULL; + + hwnd = CreateWindow (MAKEINTRESOURCE (klass), + NULL, WS_POPUP, + 0, 0, 0, 0, NULL, NULL, + _gdk_app_hmodule, NULL); + if (!hwnd) + goto failed; + + SetLastError (0); + _hwnd_next_viewer = SetClipboardViewer (hwnd); + + if (_hwnd_next_viewer == NULL && GetLastError() != 0) + goto failed; + + /* FIXME: http://msdn.microsoft.com/en-us/library/ms649033(v=VS.85).aspx */ + /* This is only supported by Vista, and not yet by mingw64 */ + /* if (AddClipboardFormatListener (hwnd) == FALSE) */ + /* goto failed; */ + + return hwnd; + +failed: + g_critical ("Failed to install clipboard viewer"); + UnregisterClass (MAKEINTRESOURCE (klass), _gdk_app_hmodule); + return NULL; } static gboolean gdk_win32_display_request_selection_notification (GdkDisplay *display, - GdkAtom selection) + GdkAtom selection) { - return FALSE; + static HWND hwndViewer = NULL; + gboolean ret = FALSE; + + GDK_NOTE (DND, + g_print ("gdk_display_request_selection_notification (..., %s)", + gdk_atom_name (selection))); + + if (selection == GDK_SELECTION_CLIPBOARD || + selection == GDK_SELECTION_PRIMARY) + { + if (!hwndViewer) + { + hwndViewer = _gdk_win32_register_clipboard_notification (); + GDK_NOTE (DND, g_print (" registered")); + } + ret = (hwndViewer != NULL); + } + else + { + GDK_NOTE (DND, g_print (" unsupported")); + ret = FALSE; + } + + GDK_NOTE (DND, g_print (" -> %s\n", ret ? "TRUE" : "FALSE")); + return ret; } static gboolean diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index 3e98315e32..cc19f962c1 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -100,7 +100,6 @@ static gboolean gdk_event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data); -static void append_event (GdkEvent *event); static gboolean is_modally_blocked (GdkWindow *window); /* Private variable declarations @@ -206,7 +205,7 @@ generate_focus_event (GdkDeviceManager *device_manager, event->focus_change.in = in; gdk_event_set_device (event, device); - append_event (event); + _gdk_win32_append_event (event); } static void @@ -230,7 +229,7 @@ generate_grab_broken_event (GdkDeviceManager *device_manager, event->grab_broken.grab_window = grab_window; gdk_event_set_device (event, device); - append_event (event); + _gdk_win32_append_event (event); } static LRESULT @@ -895,8 +894,8 @@ fixup_event (GdkEvent *event) event->any.send_event = InSendMessage (); } -static void -append_event (GdkEvent *event) +void +_gdk_win32_append_event (GdkEvent *event) { GList *link; @@ -1164,13 +1163,13 @@ synthesize_enter_or_leave_event (GdkWindow *window, event->crossing.state = 0; /* FIXME: Set correctly */ gdk_event_set_device (event, _gdk_display->core_pointer); - append_event (event); + _gdk_win32_append_event (event); if (type == GDK_ENTER_NOTIFY && window->extension_events != 0) _gdk_device_wintab_update_window_coords (window); } - + /* The check_extended flag controls whether to check if the windows want * events from extended input devices and if the message should be skipped * because an extended input device is active @@ -1329,7 +1328,7 @@ handle_configure_event (MSG *msg, event->configure.x = point.x; event->configure.y = point.y; - append_event (event); + _gdk_win32_append_event (event); } } @@ -1547,7 +1546,7 @@ generate_button_event (GdkEventType type, event->button.button = button; gdk_event_set_device (event, _gdk_display->core_pointer); - append_event (event); + _gdk_win32_append_event (event); } static void @@ -1982,7 +1981,7 @@ gdk_event_translate (MSG *msg, if (msg->wParam == VK_MENU) event->key.state &= ~GDK_MOD1_MASK; - append_event (event); + _gdk_win32_append_event (event); return_val = TRUE; break; @@ -2057,7 +2056,7 @@ gdk_event_translate (MSG *msg, gdk_event_set_device (event, GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_keyboard); build_wm_ime_composition_event (event, msg, wbuf[i], key_state); - append_event (event); + _gdk_win32_append_event (event); } if (window->event_mask & GDK_KEY_RELEASE_MASK) @@ -2068,7 +2067,7 @@ gdk_event_translate (MSG *msg, gdk_event_set_device (event, GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_keyboard); build_wm_ime_composition_event (event, msg, wbuf[i], key_state); - append_event (event); + _gdk_win32_append_event (event); } } return_val = TRUE; @@ -2193,7 +2192,7 @@ gdk_event_translate (MSG *msg, event->motion.is_hint = FALSE; gdk_event_set_device (event, _gdk_display->core_pointer); - append_event (event); + _gdk_win32_append_event (event); return_val = TRUE; break; @@ -2264,7 +2263,7 @@ gdk_event_translate (MSG *msg, event->scroll.state = build_pointer_event_state (msg); gdk_event_set_device (event, _gdk_display->core_pointer); - append_event (event); + _gdk_win32_append_event (event); return_val = TRUE; break; @@ -2424,7 +2423,7 @@ gdk_event_translate (MSG *msg, event = gdk_event_new (msg->wParam ? GDK_MAP : GDK_UNMAP); event->any.window = window; - append_event (event); + _gdk_win32_append_event (event); if (event->any.type == GDK_UNMAP) { @@ -2894,7 +2893,7 @@ gdk_event_translate (MSG *msg, event = gdk_event_new (GDK_DELETE); event->any.window = window; - append_event (event); + _gdk_win32_append_event (event); impl = GDK_WINDOW_IMPL_WIN32 (window->impl); @@ -2926,7 +2925,7 @@ gdk_event_translate (MSG *msg, event = gdk_event_new (GDK_DESTROY); event->any.window = window; - append_event (event); + _gdk_win32_append_event (event); return_val = TRUE; break; @@ -2942,7 +2941,7 @@ gdk_event_translate (MSG *msg, event->selection.window = window; event->selection.selection = GDK_SELECTION_CLIPBOARD; event->selection.time = _gdk_win32_get_next_tick (msg->time); - append_event (event); + _gdk_win32_append_event (event); } else { @@ -2962,7 +2961,7 @@ gdk_event_translate (MSG *msg, } /* We need to render to clipboard immediately, don't call - * append_event() + * _gdk_win32_append_event() */ event = gdk_event_new (GDK_SELECTION_REQUEST); event->selection.window = window; @@ -3075,7 +3074,7 @@ gdk_event_translate (MSG *msg, g_object_ref (window); if (_gdk_input_other_event (event, msg, window)) - append_event (event); + _gdk_win32_append_event (event); else gdk_event_free (event); diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h index 88d9f87afb..2f320f15b0 100644 --- a/gdk/win32/gdkprivate-win32.h +++ b/gdk/win32/gdkprivate-win32.h @@ -493,6 +493,7 @@ GdkAtom _gdk_win32_display_manager_atom_intern (GdkDisplayManager *manager, gint only_if_exists); gchar *_gdk_win32_display_manager_get_atom_name (GdkDisplayManager *manager, GdkAtom atom); +void _gdk_win32_append_event (GdkEvent *event); /* Initialization */ void _gdk_win32_windowing_init (void); -- 2.30.2